/*! *********************************************************************************
* Copyright (c) 2015, Freescale Semiconductor, Inc.
* Copyright 2016-2017 NXP
* All rights reserved.
*
* \file
*
* Keyboard implementation file for ARM CORTEX-M4 processor.
* The keyboard handling logic can understand one or more keys 
* pressed simultaneously. 
*
* SPDX-License-Identifier: BSD-3-Clause
********************************************************************************** */

#include "EmbeddedTypes.h"
#include "TimersManager.h"
#include "GPIO_Adapter.h"

#include "fsl_os_abstraction.h"
#include "Keyboard.h"





/******************************************************************************
*******************************************************************************
* Private macros
*******************************************************************************
******************************************************************************/

/*
 * Hardware settings
 */


/* Masks for keyboard Switches */
#define gSWITCH1_MASK_c                 0x01
#define gSWITCH2_MASK_c                 0x02
#define gSWITCH3_MASK_c                 0x04
#define gSWITCH4_MASK_c                 0x08


#if gKeyBoardSupported_d
/*
 * Name: mNoKey_c
 * Description: no key macro definition
 */
#define mNoKey_c                        0xFF
#endif /* gKeyBoardSupported_d */


/******************************************************************************
*******************************************************************************
* Private type definitions
*******************************************************************************
******************************************************************************/

/* 
 * Name: KeyState_t
 * Description: enumerated data type for key states
 */
#if (gKeyEventNotificationMode_d == gKbdEventShortLongPressMode_c) /* ACTIVE */

typedef enum tag_KeyState
{
    mStateKeyIdle,        /* coming in for first time */
    mStateKeyDetected,    /* got a key, waiting to see if it's a long key */
    mStateKeyWaitRelease  /* got the long key, waiting for the release to go back to idle */
}KeyState_t;
#endif /* gKeyBoardSupported_d*/

#if gKeyBoardSupported_d
/*
 * Name:
 * Description: switches scan result definition 
 */
typedef uint32_t    switchScan_t;   /* 按键扫描的结果 */
#endif /* gKeyBoardSupported_d */

/******************************************************************************
*******************************************************************************
* Private memory declarations
*******************************************************************************
******************************************************************************/
#if (gKeyBoardSupported_d) && (gKBD_KeysCount_c)
kbdSwitchConfig_t kbdSwButtons[gKBD_KeysCount_c];
#endif



/******************************************************************************
*******************************************************************************
* Private prototypes
*******************************************************************************
******************************************************************************/

#if gKeyBoardSupported_d
/******************************************************************************
 * Name: KbGpioInit
 * Description: Initialize the GPIOs used by the keyboard (switches)
 * Parameter(s): -
 * Return: -
 ******************************************************************************/
static void KbGpioInit
(
void
);



#if gTMR_Enabled_d

/******************************************************************************
 * Name: KBD_KeySwitchPortGet
 * Description: Gets the switch port
 * Parameter(s): -
 * Return: switch port value (pressed / not pressed keys)
 ******************************************************************************/
static switchScan_t KBD_KeySwitchPortGet
(
void
);

/******************************************************************************
 * Name: KBD_KeyCheck
 * Description: Called to check if a key is still pressed
 * Parameter(s): [IN] previousPressed - previously pressed key
 * Return: TRUE if the key passed as argument is still pressed, FALSE otherwise
 ******************************************************************************/
#if ((gKeyEventNotificationMode_d == gKbdEventShortLongPressMode_c) ||  \
     (gKeyEventNotificationMode_d == gKbdEventPressHoldReleaseMode_c))
static bool_t KBD_KeyCheck
(
switchScan_t previousPressed
);
#endif /* #if ((gKeyEventNotificationMode_d == gKbdEventShortLongPressMode_c) ||    \
               (gKeyEventNotificationMode_d == gKbdEventPressHoldReleaseMode_c)) */

/******************************************************************************
 * Name: KeyScan
 * Description: scan the keyboard switches and detects key press/hold/release 
 *              or short/long press
 * Parameter(s): [IN]timerId - key scan timer ID
 * Return: -
 ******************************************************************************/
static void KeyScan
(
uint8_t timerId
);
#endif /* #if gTMR_Enabled_d */


#endif /* gKeyBoardSupported_d */

/******************************************************************************
*******************************************************************************
* Private memory declarations
*******************************************************************************
******************************************************************************/

#if gKeyBoardSupported_d

#if (gKeyEventNotificationMode_d == gKbdEventPressOnly_c)
/*
 * Name: mKeyPressed
 * Description: Keys pressed mask; each bit represents a key / electrode; 
 *             (i.e. bit0 -> KEY0, bit1 -> KEY1, etc)  
 */
static volatile uint16_t mKeyPressed;
#endif /* gKeyEventNotificationMode_d == gKbdEventPressOnly_c */

#if (gKeyEventNotificationMode_d == gKbdEventPressHoldReleaseMode_c)

#if gKeyBoardSupported_d
#if gTMR_Enabled_d
/*
 * Name: mKbdHoldDectionKeyCount
 * Description: counter used for key hold detection
 */
static uint16_t       mKbdHoldDectionKeyCount;

/*
 * Name: mKbdHoldGenKeyCount
 * Description: counter used for key hold generation
 */
static uint16_t       mKbdHoldGenKeyCount;
#endif /* #if gTMR_Enabled_d */
#endif /* gKeyBoardSupported_d */
#endif /* gKeyEventNotificationMode_d == gKbdEventPressHoldReleaseMode_c */

#if ( (gKeyEventNotificationMode_d == gKbdEventShortLongPressMode_c) || (gKeyEventNotificationMode_d == gKbdEventPressHoldReleaseMode_c) ) 

#if gKeyBoardSupported_d
#if gTMR_Enabled_d
/*
 * Name: mSwitch_SCAN
 * Description: switch scan result
 */
static uint32_t mSwitch_SCAN;

#if (gKeyEventNotificationMode_d == gKbdEventShortLongPressMode_c)
/*
 * Name: mKeyState
 * Description: key scan state variable used in the scanning state machine
 */
static uint8_t mKeyState = mStateKeyIdle;

/*
 * Name: mLongKeyCount
 * Description: self explanatory
 */
static uint32_t mLongKeyCount;
#elif (gKeyEventNotificationMode_d == gKbdEventPressHoldReleaseMode_c)
/*
 * Name: mKeyState
 * Description: key scan state variable used in the scanning state machine
 */
static uint8_t mKeyState = mStateKeyDebounce;

#endif /* gKeyEventNotificationMode_d */
#endif /* #if gTMR_Enabled_d */
#endif /* gKeyBoardSupported_d */
#endif /* (gKeyEventNotificationMode_d == gKbdEventShortLongPressMode_c) || (gKeyEventNotificationMode_d == gKbdEventPressHoldReleaseMode_c) */

/*
 * Name: pressedKey
 * Description: pressed key number
 */
#if gKeyBoardSupported_d
#if gTMR_Enabled_d
static uint8_t pressedKey;
#endif
#endif

/*
 * Name: mKeyScanTimerID
 * Description: timer ID used for key scanning
 */
tmrTimerID_t mKeyScanTimerID = gTmrInvalidTimerID_c;

/*
 * Name: mpfKeyFunction
 * Description: pointer to the application callback function
 */
static KBDFunction_t mpfKeyFunction = NULL;  /* 键盘需要固定周期扫描，故该回调函数会放在定时器任务中 */

#endif /* gKeyBoardSupported_d */

//Gpio_t SwitchPin[] = {
//                        { 
//                            {.PortClockFunc = RCC_AHB1PeriphClockCmd},
//                            {.PeriphClockFunc = RCC_APB2PeriphClockCmd},
//                            {.Gpio_x = GPIOA},
//                            {.GpioCfg.GPIO_Mode = GPIO_Mode_IN,
//                             .GpioCfg.GPIO_PuPd = GPIO_PuPd_DOWN,
//                             .GpioCfg.GPIO_Pin = GPIO_Pin_0},
//                            {.AFCfg.ExitCfg.EXTI_Line = EXTI_Line0,
//                             .AFCfg.ExitCfg.EXTI_Mode = EXTI_Mode_Interrupt,
//                             .AFCfg.ExitCfg.EXTI_Trigger = EXTI_Trigger_Rising,
//                             .AFCfg.ExitCfg.EXTI_LineCmd = ENABLE
//                            },
//                            {.NvicCfg.NVIC_IRQChannel =  EXTI0_IRQn,
//                             .NvicCfg.NVIC_IRQChannelPreemptionPriority = 0x0F, 
//                             .NvicCfg.NVIC_IRQChannelSubPriority = 0x0F, 
//                             .NvicCfg.NVIC_IRQChannelCmd = ENABLE 
//                            }
//                        },
//                    };



/******************************************************************************
*******************************************************************************
* Private functions
*******************************************************************************
******************************************************************************/
#if gKeyBoardSupported_d
/******************************************************************************
 * Name: KbGpioInit
 * Description: Initialize the GPIOs used by the keyboard (switches)
 * Parameter(s): -
 * Return: -
 ******************************************************************************/
static void KbGpioInit
(
void
)
{
    STM_EVAL_PBInit(BUTTON_USER, BUTTON_MODE_EXTI);
}



/******************************************************************************
* Name: KBD_Deinit
* Description: -
* Parameter(s): -
* Return: -
******************************************************************************/
void KBD_Deinit
(
void
)
{
    uint8_t i;
    TMR_FreeTimer(mKeyScanTimerID);
    
    for( i=0; i<gKBD_KeysCount_c; i++ )
    {
        if(kbdSwButtons[i].swType == gKBDTypeGpio_c)
        {
            //GpioUninstallIsr( kbdSwButtons[i].config_struct.pSwGpio );
        }
    }

}

#if gTMR_Enabled_d

/******************************************************************************
* Name: KBD_KeySwitchPortGet
* Description: Gets the switch port
* Parameter(s): -
* Return: switch port value (pressed / not pressed keys)
******************************************************************************/
static switchScan_t KBD_KeySwitchPortGet
(
void
)
{

    uint32_t portScan;
    pressedKey = mNoKey_c;  /* 记录被按下按键的数据 */

    portScan = 0;

    if(Bit_SET == GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0))
    {
        portScan |= (1u << 0);
    }
        

    if(portScan != 0 && pressedKey == mNoKey_c)/*first detected key*/
    {
        pressedKey = 1;  /* 按键数量 */
    }

    return portScan;
}

/******************************************************************************
 * Name: KBD_KeyCheck
 * Description: Called to check if a key is still pressed
 * Parameter(s): [IN] previousPressed - previously pressed key
 * Return: TRUE if the key passed as argument is still pressed, FALSE otherwise
 ******************************************************************************/
#if ((gKeyEventNotificationMode_d == gKbdEventShortLongPressMode_c) ||  \
    (gKeyEventNotificationMode_d == gKbdEventPressHoldReleaseMode_c))
static bool_t KBD_KeyCheck
(
switchScan_t previousPressed
)
{
    bool_t pressed = FALSE;
    uint32_t portScan, i;
    uint8_t key = mNoKey_c;

    
    previousPressed = previousPressed; /* avoid compiler warnings */

    portScan = 0;
    for( i=0; i<gKBD_KeysCount_c; i++ )
    {
        
        if(Bit_SET == GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0))
        {
            portScan |= (1u << i);
        }

        if(portScan != 0 && key == mNoKey_c)/*first detected key*/
        {
            key = 1;
        }
    }
    /* Check if the switch is still pressed */
    if(pressedKey == key)
    {
        pressed = TRUE;
    }

    return pressed;
}
#endif /*#if ((gKeyEventNotificationMode_d == gKbdEventShortLongPressMode_c) || \
    (gKeyEventNotificationMode_d == gKbdEventPressHoldReleaseMode_c))*/


/******************************************************************************
 * Name: KeyScan
 * Description: scan the keyboard switches and detects key press/hold/release 
 *              or short/long press
 * Parameter(s): [IN]timerId - key scan timer ID
 * Return: -
 ******************************************************************************/
#if (gKeyEventNotificationMode_d == gKbdEventPressOnly_c)
static void KeyScan
(
uint8_t timerId
)
{   
    if(KBD_KeySwitchPortGet())
    {
        TMR_StopTimer(timerId);

        mpfKeyFunction(1<<pressedKey); /* bits 0..3 are for keyboard */
    }   
}

#elif (gKeyEventNotificationMode_d == gKbdEventShortLongPressMode_c)
static void KeyScan
(
uint8_t timerId
)
{
    uint8_t keyBase;
    uint32_t portScan, i;

    
    switch(mKeyState) 
    {
        
        /* got a fresh key */
        case mStateKeyIdle:      
            mSwitch_SCAN = KBD_KeySwitchPortGet();
            if(mSwitch_SCAN != 0)
            {        
                mKeyState = mStateKeyDetected;
                mLongKeyCount = 0;
            }
            else
            {
                TMR_StopTimer(timerId); 
            }
            break;

        /* a key was detected. Has it been released or still being pressed? */
        case mStateKeyDetected:
            keyBase = 0;  /* assume no key */

            /* Check to see if the key is still pressed. Ignore other pressed keys */
            if( KBD_KeyCheck(mSwitch_SCAN) ) 
            {
                mLongKeyCount++;
            }
            else 
            {     
                /* short key press */
                keyBase = gKBD_EventPB1_c;
            
                /* validate long key count*/
                if((mLongKeyCount >= gKbdLongKeyIterations_c) &&
                   (mLongKeyCount < gKbdVeryLongKeyIterations_c))
                {
                    keyBase = gKBD_EventLongPB1_c;
                }
                if(mLongKeyCount >= gKbdVeryLongKeyIterations_c)
                {
                    keyBase = gKBD_EventVeryLongPB1_c;
                }                     
            }

            if(keyBase) 
            {       
                /* if a key was pressed, send it */
                if(pressedKey != mNoKey_c)
                {
                    /* 此处可以调用应用层对按键处理的回调函数 */
                    mpfKeyFunction(keyBase + pressedKey);
                }

                /* whether we sent a key or not, wait to go back to keyboard  */
                mKeyState = mStateKeyWaitRelease;
            }
            break;

        /* got the long key, waiting for the release to go back to idle */
        case mStateKeyWaitRelease:      
            /* wait for the release before going back to idle */  
            portScan=0;
            for( i=0; i<gKBD_KeysCount_c; i++ )
            {
                if(Bit_SET == GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0))
                {
                    portScan |= (1u << i);
                }
            }
            if((mSwitch_SCAN == 0) || (portScan == 0)) 
            {
                mKeyState = mStateKeyIdle;
                TMR_StopTimer(timerId);                
            }
            break;        
        default:
            break;
    }
}

#elif (gKeyEventNotificationMode_d == gKbdEventPressHoldReleaseMode_c)
static void KeyScan
(
uint8_t timerId
)
{
    uint32_t portScan, i;

    
    switch(mKeyState) 
    {    
    case mStateKeyDebounce:
        mSwitch_SCAN = KBD_KeySwitchPortGet();
        if(mSwitch_SCAN != 0) 
        {
            mKeyState =  mStateKeyHoldDetection;
            mKbdHoldDectionKeyCount = 0; 
            //Generate press event indication     
            if(pressedKey != mNoKey_c) 
            {
                mpfKeyFunction(gKBD_EventPressPB1_c + pressedKey);           
            }
        } 
        else 
        {
            TMR_StopTimer(timerId);
        }      
        break;
    case mStateKeyHoldDetection:
        if( KBD_KeyCheck(mSwitch_SCAN) ) 
        {
            mKbdHoldDectionKeyCount++;        
            if (mKbdHoldDectionKeyCount >= gKbdFirstHoldDetectIterations_c) 
            {
                //first hold event detected - generate hold event
                if(pressedKey != mNoKey_c) 
                {
                    mpfKeyFunction(gKBD_EventHoldPB1_c + pressedKey);                       
                }                    
                mKbdHoldGenKeyCount = 0;
                mKeyState = mStateKeyHoldGen;
            }
        } 
        else 
        {
            for( i=0, portScan = 0; i<gKBD_KeysCount_c; i++ )
            {
                if(kbdSwButtons[i].swType == gKBDTypeGpio_c)
                {
                    if(GpioReadPinInput(kbdSwButtons[i].config_struct.pSwGpio) == 0)
                    {
                        portScan |= (1u << i);
                    }
                }

            }
            
            if((mSwitch_SCAN == 0) || (portScan == 0)) 
            {
                if(pressedKey != mNoKey_c) 
                {
                    mpfKeyFunction(gKBD_EventReleasePB1_c + pressedKey);                       
                    mKeyState = mStateKeyDebounce;
                    TMR_StopTimer(timerId);            
                }                                       
            }                
        }
        break;
    case mStateKeyHoldGen:
        if( KBD_KeyCheck(mSwitch_SCAN) ) 
        {
            mKbdHoldGenKeyCount++;
            if(mKbdHoldGenKeyCount >= gKbdHoldDetectIterations_c) 
            {
                mKbdHoldGenKeyCount = 0;
                if(pressedKey != mNoKey_c) 
                {
                    mpfKeyFunction(gKBD_EventHoldPB1_c + pressedKey);
                }           
            }
        } 
        else 
        {
            for( i=0, portScan = 0; i<gKBD_KeysCount_c; i++ )
            {
                if(kbdSwButtons[i].swType == gKBDTypeGpio_c)
                {
                    if(GpioReadPinInput(kbdSwButtons[i].config_struct.pSwGpio) == 0)
                    {
                        portScan |= (1u << i);
                    }
                }

            }
            
            if((mSwitch_SCAN == 0) || (portScan == 0)) 
            {
                if(pressedKey != mNoKey_c) 
                {
                    mpfKeyFunction(gKBD_EventReleasePB1_c + pressedKey);

                    mKeyState = mStateKeyDebounce;
                    TMR_StopTimer(timerId);            
                }                                       
            }                     
        }
        break;
    default:
        break;
    }
}
#endif /* gKeyEventNotificationMode_d */
#endif /* gTMR_Enabled_d */


#endif /* gKeyBoardSupported_d */

/******************************************************************************
*******************************************************************************
* Public functions
*******************************************************************************/

#if gKeyBoardSupported_d

/******************************************************************************
 * Name: KBD_Init
 * Description: Initializes the keyboard module internal variables 
 * Parameter(s): [IN] pfCallBackAdr - pointer to application callback function
 * Return: -
 * Notes: It the TIMER platform component is enabled, TMR_Init() function MUST
 *        be called before KBD_Init() function
 ******************************************************************************/
void KBD_Init( KBDFunction_t pfCallBackAdr )  /* 逻辑上的键盘初始化和MCU GPIO初始化 */
{
    /* if no valid pointer provided, return */
    //if(NULL != pfCallBackAdr) 
    {
        /* store the pointer to callback function provided by the application */
        mpfKeyFunction = pfCallBackAdr;  /* 全局函数指针类型变量初始化 */
        
#if gTMR_Enabled_d
        /* timer is used to determine short or long key press */
        mKeyScanTimerID = TMR_AllocateTimer();  /* 什么要给键盘这个软件实体分配软件定时器？软件定时器的原理是什么？ */
#endif /* #if gTMR_Enabled_d */    
        

        /* initialize all the GPIO pins for keyboard */
        KbGpioInit();  /* 按键的GPIO模块初始化 */
        
    }
}
#endif /* gKeyBoardSupported_d */



/******************************************************************************
 * Name: KBD_PrepareEnterLowPower
 * Description:
 * Parameter(s): -
 * Return: -
 ******************************************************************************/
#if gKeyBoardSupported_d
void KBD_PrepareEnterLowPower(void)
{

}
#endif







void EXTI0_IRQHandler(void)
{
    if(SET == EXTI_GetFlagStatus(EXTI_Line0))
    {
        EXTI_ClearFlag(EXTI_Line0);
        TMR_StartIntervalTimer(mKeyScanTimerID, 
                               gKeyScanInterval_c, 
                               (pfTmrCallBack_t)KeyScan, /* 按键扫描的软件定时器的回调函数 */
                               (void*)((uint32_t)mKeyScanTimerID));
    }
}



